package net.xiake6.orm.datasource.sharding;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.commons.lang3.math.NumberUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;
import net.xiake6.orm.datasource.HandleDataSource;

/**
 * 执行mapper方法之前的切面 <br>
 * 获取datasource对象之前往HandleDataSource中指定当前线程数据源路由的key <br>
 * ClassName:DataSourceAspect <br/>
 * Date: 2017年8月16日 上午9:09:34 <br/>
 * 
 * @author fenglibin
 * @version
 * @see
 */
@Slf4j
@Service
public class ShardingDataSourceAspect {

	@Autowired
	private ShardingConfig shardingConfig;

	@Resource(name = "targetDataSources")
	/**
	 * 目标数据源
	 */
	private Map<String, javax.sql.DataSource> targetDataSources;

	/**
	 * 按主从分后的数据源
	 */
	private Map<Integer, String> dataSources = new HashMap<Integer, String>();

	/**
	 * 拆分主从数据源
	 * 
	 * @param targetDataSources
	 */
	@PostConstruct
	public void setTargetDataSources() {
		if (targetDataSources != null && targetDataSources.size() > 0) {
			for (Map.Entry<String, javax.sql.DataSource> dataSource : targetDataSources.entrySet()) {
				String name = dataSource.getKey();
				String[] nameSplit = name.split("_");
				if (nameSplit.length == 2 && NumberUtils.isCreatable(nameSplit[1])) {
					dataSources.put(Integer.parseInt(nameSplit[1]), name);
				}
			}
		}
	}

	/**
	 * 在mapper层方法之前获取datasource对象之前在切面中指定当前线程数据源路由的key
	 * 
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */
	public void before(JoinPoint point) {

		Object target = point.getTarget();
		log.debug("target:" + target.toString());
		String method = point.getSignature().getName();
		log.debug("method:" + method);
		try {
			Method m = ((MethodSignature) point.getSignature()).getMethod();
			if (m != null) {
				Map<String, Object> objFieldNameAndValue = fieldNameAndValue(point);
				log.info(objFieldNameAndValue.toString());
				if (shardingConfig.getDatabaseShardingCondition() != null) {
					int databaseShardingId = shardingConfig.getDatabaseShardingCondition()
							.getShardingId(objFieldNameAndValue);
					HandleDataSource.putDataSource(dataSources.get(databaseShardingId));
				}
			}
		} catch (Exception e) {
			log.error("Aspect datasource error happened:" + e.getMessage(), e);
		}
	}

	/**
	 * 从被执行切面的方法中获取参数对象，遍历这些对象中定义的字段，<br>
	 * 并取出这些字段其值组成Key-Value的Map做为结果返回
	 * 
	 * @param m
	 * @return
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */
	private Map<String, Object> fieldNameAndValue(JoinPoint point)
			throws IllegalArgumentException, IllegalAccessException {
		final Map<String, Object> params = new HashMap<String, Object>();
		Object[] args = point.getArgs();
		AtomicInteger index = new AtomicInteger(0);
		for (Object obj : args) {
			getParms(params, obj, index);
		}
		return params;
	}

	private void getParms(final Map<String, Object> params, Object obj, AtomicInteger index)
			throws IllegalArgumentException, IllegalAccessException {
		if (isBasicType(obj)) {
			params.put("key_" + index.get(), obj.toString());
			index.addAndGet(1);
		} else if (obj instanceof Map || obj instanceof HashMap) {
			params.putAll((Map) obj);
		} else if (obj instanceof List || obj instanceof ArrayList) {
			List list = (List) obj;
			for (Object o : list) {
				getParms(params, o, index);
			}
		} else {// 如果不是以上类型，则默认为用户自定义的bean
			Field[] fields = obj.getClass().getDeclaredFields();
			for (Field field : fields) {
				field.setAccessible(true);
				params.put(field.getName(), field.get(obj));
			}
		}
	}

	/**
	 * 判断当前对象是否基础类型
	 * 
	 * @param obj
	 * @return
	 */
	private boolean isBasicType(Object obj) {
		if (obj instanceof String || obj instanceof Integer || obj instanceof Long || obj instanceof Float
				|| obj instanceof Double || obj instanceof Short || obj instanceof Character) {
			return true;
		}
		return false;
	}
}
